module Spec.Chacha20Poly1305.Test
#reset-options "--z3rlimit 100 --initial_fuel 0 --max_fuel 0 --initial_ifuel 0"

open FStar.Mul
open Lib.IntTypes
open Lib.RawIntTypes
open Lib.Sequence
open Lib.ByteSequence
open Spec.Chacha20Poly1305

(* Tests: RFC7539 *)

let test_key = List.Tot.map u8_from_UInt8 [
         0x80uy;0x81uy;0x82uy;0x83uy;0x84uy;0x85uy;0x86uy;0x87uy;
         0x88uy;0x89uy;0x8auy;0x8buy;0x8cuy;0x8duy;0x8euy;0x8fuy;
	 0x90uy;0x91uy;0x92uy;0x93uy;0x94uy;0x95uy;0x96uy;0x97uy;
         0x98uy;0x99uy;0x9auy;0x9buy;0x9cuy;0x9duy;0x9euy;0x9fuy]

let test_nonce = List.Tot.map u8_from_UInt8 [
         0x07uy;0x00uy;0x00uy;0x00uy;0x40uy;0x41uy;0x42uy;0x43uy;
         0x44uy;0x45uy;0x46uy;0x47uy]

let test_plaintext = List.Tot.map u8_from_UInt8 [
         0x4cuy; 0x61uy; 0x64uy; 0x69uy; 0x65uy; 0x73uy; 0x20uy; 0x61uy;
    	 0x6euy; 0x64uy; 0x20uy; 0x47uy; 0x65uy; 0x6euy; 0x74uy; 0x6cuy;
    	 0x65uy; 0x6duy; 0x65uy; 0x6euy; 0x20uy; 0x6fuy; 0x66uy; 0x20uy;
    	 0x74uy; 0x68uy; 0x65uy; 0x20uy; 0x63uy; 0x6cuy; 0x61uy; 0x73uy;
    	 0x73uy; 0x20uy; 0x6fuy; 0x66uy; 0x20uy; 0x27uy; 0x39uy; 0x39uy;
    	 0x3auy; 0x20uy; 0x49uy; 0x66uy; 0x20uy; 0x49uy; 0x20uy; 0x63uy;
   	 0x6fuy; 0x75uy; 0x6cuy; 0x64uy; 0x20uy; 0x6fuy; 0x66uy; 0x66uy;
    	 0x65uy; 0x72uy; 0x20uy; 0x79uy; 0x6fuy; 0x75uy; 0x20uy; 0x6fuy;
    	 0x6euy; 0x6cuy; 0x79uy; 0x20uy; 0x6fuy; 0x6euy; 0x65uy; 0x20uy;
    	 0x74uy; 0x69uy; 0x70uy; 0x20uy; 0x66uy; 0x6fuy; 0x72uy; 0x20uy;
    	 0x74uy; 0x68uy; 0x65uy; 0x20uy; 0x66uy; 0x75uy; 0x74uy; 0x75uy;
    	 0x72uy; 0x65uy; 0x2cuy; 0x20uy; 0x73uy; 0x75uy; 0x6euy; 0x73uy;
    	 0x63uy; 0x72uy; 0x65uy; 0x65uy; 0x6euy; 0x20uy; 0x77uy; 0x6fuy;
    	 0x75uy; 0x6cuy; 0x64uy; 0x20uy; 0x62uy; 0x65uy; 0x20uy; 0x69uy;
    	 0x74uy; 0x2euy]

let test_aad = List.Tot.map u8_from_UInt8 [
           0x50uy;0x51uy;0x52uy;0x53uy;0xc0uy;0xc1uy;0xc2uy;0xc3uy;
           0xc4uy;0xc5uy;0xc6uy;0xc7uy]

let test_cipher = List.Tot.map u8_from_UInt8 [
               0xd3uy;0x1auy;0x8duy;0x34uy;0x64uy;0x8euy;0x60uy;0xdbuy;
               0x7buy;0x86uy;0xafuy;0xbcuy;0x53uy;0xefuy;0x7euy;0xc2uy;
	       0xa4uy;0xaduy;0xeduy;0x51uy;0x29uy;0x6euy;0x08uy;0xfeuy;
               0xa9uy;0xe2uy;0xb5uy;0xa7uy;0x36uy;0xeeuy;0x62uy;0xd6uy;
	       0x3duy;0xbeuy;0xa4uy;0x5euy;0x8cuy;0xa9uy;0x67uy;0x12uy;
               0x82uy;0xfauy;0xfbuy;0x69uy;0xdauy;0x92uy;0x72uy;0x8buy;
	       0x1auy;0x71uy;0xdeuy;0x0auy;0x9euy;0x06uy;0x0buy;0x29uy;
               0x05uy;0xd6uy;0xa5uy;0xb6uy;0x7euy;0xcduy;0x3buy;0x36uy;
	       0x92uy;0xdduy;0xbduy;0x7fuy;0x2duy;0x77uy;0x8buy;0x8cuy;
               0x98uy;0x03uy;0xaeuy;0xe3uy;0x28uy;0x09uy;0x1buy;0x58uy;
	       0xfauy;0xb3uy;0x24uy;0xe4uy;0xfauy;0xd6uy;0x75uy;0x94uy;
               0x55uy;0x85uy;0x80uy;0x8buy;0x48uy;0x31uy;0xd7uy;0xbcuy;
	       0x3fuy;0xf4uy;0xdeuy;0xf0uy;0x8euy;0x4buy;0x7auy;0x9duy;
               0xe5uy;0x76uy;0xd2uy;0x65uy;0x86uy;0xceuy;0xc6uy;0x4buy;
	       0x61uy;0x16uy;]

let test_mac = List.Tot.map u8_from_UInt8 [0x1auy;0xe1uy;0x0buy;0x59uy;0x4fuy;0x09uy;0xe2uy;0x6auy;
            0x7euy;0x90uy;0x2euy;0xcbuy;0xd0uy;0x60uy;0x06uy;0x91uy]

let test() =
  assert_norm(List.Tot.length test_key = 32);
  assert_norm(List.Tot.length test_nonce = 12);
  assert_norm(List.Tot.length test_plaintext = 114);
  assert_norm(List.Tot.length test_aad = 12);
  assert_norm(List.Tot.length test_cipher = 114);
  assert_norm(List.Tot.length test_mac = 16);
  let test_key: key = of_list test_key in
  let test_nonce:nonce = of_list test_nonce in
  let test_plaintext: lbytes 114 = of_list test_plaintext in
  let test_aad: lbytes 12 = of_list test_aad in
  let test_cipher : lbytes 114 = of_list test_cipher in
  let test_mac : lbytes 16 = of_list test_mac in
  let enc =  aead_encrypt test_key test_nonce test_plaintext test_aad in
  let cipher = Seq.slice enc 0 114 in
  let mac = Seq.slice enc 114 130 in
  let dec =  aead_decrypt test_key test_nonce test_cipher test_mac test_aad in
  let result_c = for_all2 (fun a b -> uint_to_nat #U8 a = uint_to_nat #U8 b) cipher test_cipher in
  let result_m = for_all2 (fun a b -> uint_to_nat #U8 a = uint_to_nat #U8 b) mac test_mac in
  let result_p = Some? dec && for_all2 (fun a b -> uint_to_nat #U8 a = uint_to_nat #U8 b) (Some?.v dec) test_plaintext in
  IO.print_string   "Expected cipher:";
  List.iter (fun a -> IO.print_string (UInt8.to_string (u8_to_UInt8 a))) (to_list test_cipher);
  IO.print_string "\nComputed cipher:";
  List.iter (fun a -> IO.print_string (UInt8.to_string (u8_to_UInt8 a))) (to_list cipher);
  IO.print_string   "\nExpected mac:";
  List.iter (fun a -> IO.print_string (UInt8.to_string (u8_to_UInt8 a))) (to_list test_mac);
  IO.print_string "\nComputed mac:";
  List.iter (fun a -> IO.print_string (UInt8.to_string (u8_to_UInt8 a))) (to_list mac);
  IO.print_string   "\nExpected plaintext:";
  List.iter (fun a -> IO.print_string (UInt8.to_string (u8_to_UInt8 a))) (to_list test_plaintext);
  IO.print_string "\nComputed plaintext:";
  if Some? dec then
    List.iter (fun a -> IO.print_string (UInt8.to_string (u8_to_UInt8 a))) (to_list (Some?.v dec));
  if result_c && result_m && result_p then begin IO.print_string "\n\nChacha20 : Success!\n"; true end
  else begin IO.print_string "\n\nChacha20: Failure :(\n"; false end
